home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / MacShell / ListControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-04  |  25.0 KB  |  1,035 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         listcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1991 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /*
  12. **
  13. ** To create a List control, you only need a single call.  For example:
  14. **
  15. **    list = CLNew(rViewCtl,            Resource ID of view control for List control.
  16. **                 numRows,            Number of rows to create List with.
  17. **                 numCols,            Number of columns to create List with.
  18. **                 cellHeight,
  19. **                 cellWidth,
  20. **                 theLProc,            Custom List procedure resource ID.
  21. **                 window,            Window to hold List control.
  22. **                 clHScroll | blBrdr | clActive        Horizontal scrollbar, active List.
  23. **    );
  24. **
  25.  
  26. ** If the CLNew call succeeds, you then have a List control in your
  27. ** window.  It will be automatically disposed of when you close the window.
  28. ** If you don't waht this to happen, then you can detatch it from the
  29. ** view control which owns it.  To do this, you would to the following:
  30. **
  31. **  viewCtl = CLViewFromList(theListHndl);
  32. **  if (viewCtl) SetCRefCon(viewCtl, nil);
  33. **
  34. ** The view control keeps a reference to the List record in the refCon.
  35. ** If the refCon is cleared, then the view control does nothing.  So, all that
  36. ** is needed to detatch a List record from a view control is to set the
  37. ** view control's refCon nil.  Now if you close the window, you will still
  38. ** have the List record.
  39. **
  40. **
  41. ** To remove a List control completely from a window, just dispose of the view
  42. ** control that holds the List record.  To do this, just do something like the below:
  43. **
  44. **  DisposeControl(CLViewFromList(theListHndl));
  45. **
  46. ** This completely disposes of the List control.
  47. **
  48. **
  49. ** Events for the List record are handled nearly automatically.  Just make the
  50. ** following call:
  51. **
  52. **  CLClick(eventPtr, &dblClick);
  53. **
  54. ** If the event was handled, true is returned.  If the event is false, then the
  55. ** event doesn't belong to a List control, and further processing of the event
  56. ** should be done.
  57. **
  58. */
  59.  
  60.  
  61. extern short    gPrintPage;        /* Non-zero means we are printing. */
  62.  
  63. /*****************************************************************************/
  64.  
  65.  
  66.  
  67. #ifndef __CONTROLS__
  68. #include <Controls.h>
  69. #endif
  70.  
  71. #ifndef __ERRORS__
  72. #include <Errors.h>
  73. #endif
  74.  
  75. #ifndef __LISTCONTROL__
  76. #include "ListControl.h"
  77. #endif
  78.  
  79. #ifndef __MEMORY__
  80. #include <Memory.h>
  81. #endif
  82.  
  83. #ifndef __PACKAGES__
  84. #include <Packages.h>
  85. #endif
  86.  
  87. #ifndef __RESOURCES__
  88. #include <Resources.h>
  89. #endif
  90.  
  91.  
  92.  
  93. /*****************************************************************************/
  94.  
  95.  
  96.  
  97. #define kListPosTextLen 32
  98.  
  99. #define kPrevSel    16
  100. #define kLeftArrow    28
  101. #define kRightArrow    29
  102. #define kUpArrow    30
  103. #define kDownArrow    31
  104.  
  105. typedef struct cdefRsrcJMP {
  106.     long    moveInst;
  107.     long    jsrInst;
  108.     short    jmpInst;
  109.     long    jmpAddress;
  110. } cdefRsrcJMP;
  111. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  112.  
  113.  
  114.  
  115. /*****************************************************************************/
  116.  
  117.  
  118.  
  119. static long            lastKeyTime;
  120. static char            listPosText[kListPosTextLen];
  121. static short        listPosTextLen;
  122. static short        theViewID;
  123.  
  124. static void            CLBorderDraw(ListHandle listHndl);
  125. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm);
  126. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen);
  127.  
  128.  
  129.  
  130.  
  131. /*****************************************************************************/
  132.  
  133.  
  134.  
  135. static ListHandle        gFoundLHndl;
  136.     /* Global value used to return info from the List control proc. */
  137.  
  138. static ControlHandle    gFoundViewCtl;
  139.     /* Global value used to return info from the List control proc. */
  140.  
  141.  
  142.  
  143. /*****************************************************************************/
  144. /*****************************************************************************/
  145. /*****************************************************************************/
  146.  
  147.  
  148.  
  149. /* Activate this List record.  Activation is not done by calling LActivate().
  150. ** The active control is indicated by the 2-pixel thick border around the
  151. ** List control.  This allows all List controls in a window to display which
  152. ** cells are selected.  This behavior can be overridden by calling LActivate()
  153. ** on the List record for List controls.
  154. ** Human interface dictates that only at most a single List control has this
  155. ** active border.  For this reason, this function scans for other List
  156. ** controls in the window and removes the border from any other that it finds.
  157. */
  158.  
  159. #pragma segment Controls
  160. void    CLActivate(Boolean active, ListHandle listHndl)
  161. {
  162.     WindowPtr        window, oldPort;
  163.     ControlHandle    viewCtl;
  164.     short            oldDisplay, newDisplay;
  165.     ListHandle        list;
  166.     CLDataHndl        listData;
  167.  
  168.     if (listHndl) {
  169.         window = (WindowPtr)(*listHndl)->port;
  170.         for (viewCtl = nil;;) {
  171.             viewCtl = CLNext(window, &list, viewCtl);
  172.             if (!viewCtl) break;
  173.             listData   = (CLDataHndl)(*viewCtl)->contrlData;
  174.             oldDisplay = (*listData)->mode;
  175.             newDisplay = (oldDisplay & (0xFFFF - clActive));
  176.             if (active)
  177.                 if (list == listHndl)
  178.                     newDisplay |= clActive;
  179.             if (oldDisplay != newDisplay) {
  180.                 (*listData)->mode = newDisplay;
  181.                 GetPort(&oldPort);
  182.                 SetPort(window);
  183.                 CLBorderDraw(list);
  184.                 SetPort(oldPort);
  185.             }
  186.         }
  187.     }
  188. }
  189.  
  190.  
  191.  
  192. /*****************************************************************************/
  193.  
  194.  
  195.  
  196. #pragma segment Controls
  197. void    CLBorderDraw(ListHandle listHndl)
  198. {
  199.     ControlHandle    viewCtl;
  200.     WindowPtr        oldPort, listPort;
  201.     short            displayInfo;
  202.     Rect            rct;
  203.     PenState        oldPen;
  204.     CLDataHndl        listData;
  205.  
  206.     if (listHndl) {
  207.         if (viewCtl = CLViewFromList(listHndl)) {
  208.             GetPort(&oldPort);
  209.             SetPort(listPort = (*listHndl)->port);
  210.             GetPenState(&oldPen);
  211.             PenNormal();
  212.             listData    = (CLDataHndl)(*viewCtl)->contrlData;
  213.             displayInfo = (*listData)->mode;
  214.             rct = (*listHndl)->rView;
  215.             InsetRect(&rct, -1, -1);
  216.             FrameRect(&rct);
  217.             if (displayInfo & clShowActive) {
  218.                 rct = (*listHndl)->rView;
  219.                 InsetRect(&rct, -4, -4);
  220.                 if ((*listHndl)->vScroll) rct.right  += 15;
  221.                 if ((*listHndl)->hScroll) rct.bottom += 15;
  222.                 PenSize(2, 2);
  223.                 if ((listPort != FrontWindow()) || (!(displayInfo & clActive))) PenPat(qd.white);
  224.                 FrameRect(&rct);
  225.             }
  226.             SetPenState(&oldPen);
  227.             SetPort(oldPort);
  228.         }
  229.     }
  230. }
  231.  
  232.  
  233.  
  234. /*****************************************************************************/
  235.  
  236.  
  237.  
  238. /* This is called when a mouseDown occurs in the content of a window.  It
  239. ** returns true if the mouseDown caused a List action to occur.  Events
  240. ** that are handled include if the user clicks on a scrollbar that is
  241. ** associated with a List control.
  242. */
  243.  
  244. #pragma segment Controls
  245. Boolean    CLClick(EventRecord *event, short *dblClick)
  246. {
  247.     WindowPtr        oldPort, window;
  248.     Point            mouseLoc;
  249.     ListHandle        list;
  250.     ControlHandle    ctlHit, viewCtl;
  251.     short            part;
  252.  
  253.     *dblClick = false;
  254.     lastKeyTime = 0;
  255.  
  256.     GetPort(&oldPort);
  257.     SetPort(window = FrontWindow());
  258.     mouseLoc = event->where;
  259.     GlobalToLocal(&mouseLoc);
  260.  
  261.     if (CLFindCtl(window, event, &list, &ctlHit)) {
  262.             /* See if the user clicked directly on the view control for a
  263.             ** List record.  If so, we definitely have some work to do.
  264.             */
  265.         if (CLFindActive(window) != list) CLActivate(true, list);
  266.             /* If user clicked on List control other than the
  267.             ** currently active control, then activate it.
  268.             */
  269.         *dblClick = LClick(mouseLoc, event->modifiers, list);
  270.         SetPort(oldPort);
  271.         return(true);
  272.     }
  273.  
  274. /* We didn't hit the view control for a List record, but don't give up yet.
  275. ** The user may be clicking on a related scrollbar.  Let's find out...
  276. */
  277.  
  278.     if (part = FindControl(mouseLoc, window, &ctlHit)) {
  279.             /* The user did click on a control.  But is it a scrollbar
  280.             ** for a List control?  Stay tuned...
  281.             */
  282.         list = CLFromScroll(ctlHit, &viewCtl);
  283.         if (list) {        /* It was a related scrollbar. */
  284.             CLActivate(true, list);
  285.             LClick(mouseLoc, event->modifiers, list);
  286.             SetPort(oldPort);
  287.             return(true);
  288.         }
  289.     }
  290.  
  291.     SetPort(oldPort);
  292.     return(false);
  293. }
  294.  
  295.  
  296.  
  297. /*****************************************************************************/
  298.  
  299.  
  300.  
  301. #pragma segment Controls
  302. pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm)
  303. {
  304. #pragma unused (varCode)
  305.  
  306.     Rect            viewRct;
  307.     ListHandle        list;
  308.     WindowPtr        thePort;
  309.  
  310.     if (list = (ListHandle)GetCRefCon(ctl)) viewRct = (*list)->rView;
  311.     else SetRect(&viewRct, 0, 0, 0, 0);
  312.  
  313.     switch (msg) {
  314.         case drawCntl:
  315.             GetPort(&thePort);
  316.             CLUpdate(thePort->visRgn, list);
  317.             CLBorderDraw(list);
  318.             break;
  319.  
  320.         case testCntl:
  321.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  322.                 gFoundViewCtl = ctl;
  323.                 gFoundLHndl   = list;
  324.                 return(1);
  325.             }
  326.             return(0);
  327.             break;
  328.  
  329.         case calcCRgns:
  330.         case calcCntlRgn:
  331.             if (msg == calcCRgns) parm &= 0x00FFFFFF;
  332.             RectRgn((RgnHandle)parm, &viewRct);
  333.             break;
  334.  
  335.         case initCntl:
  336.             break;
  337.  
  338.         case dispCntl:
  339.             if (list) {
  340.                 LDispose(list);
  341.                 DisposeHandle((Handle)(*ctl)->contrlData);
  342.             }
  343.             break;
  344.  
  345.         case posCntl:
  346.             break;
  347.  
  348.         case thumbCntl:
  349.             break;
  350.  
  351.         case dragCntl:
  352.             break;
  353.  
  354.         case autoTrack:
  355.             break;
  356.     }
  357.  
  358.     return(0);
  359. }
  360.  
  361.  
  362.  
  363. /*****************************************************************************/
  364.  
  365.  
  366.  
  367. #pragma segment Controls
  368. ControlHandle    CLCtlHit(void)
  369. {
  370.     ControlHandle    ctl;
  371.  
  372.     ctl = gFoundViewCtl;
  373.     gFoundViewCtl = nil;
  374.     return(ctl);
  375. }
  376.  
  377.  
  378.  
  379. /*****************************************************************************/
  380.  
  381.  
  382.  
  383. /* Handle the event if it applies to the active List control.  If some
  384. ** action occured due to the event, return true.
  385. */
  386.  
  387. #pragma segment Controls
  388. Boolean    CLEvent(EventRecord *event, short *dblClick)
  389. {
  390.     *dblClick = false;
  391.     if (event->what != mouseDown) return(false);
  392.     return(CLClick(event, dblClick));
  393. }
  394.  
  395.  
  396.  
  397. /*****************************************************************************/
  398.  
  399.  
  400.  
  401. #pragma segment Controls
  402. ListHandle    CLFindActive(WindowPtr window)
  403. {
  404.     ControlHandle    viewCtl;
  405.     ListHandle        list;
  406.     short            display;
  407.     CLDataHndl        listData;
  408.  
  409.     if (!window) return(nil);
  410.  
  411.     for (viewCtl = nil;;) {
  412.         viewCtl = CLNext(window, &list, viewCtl);
  413.         if (!viewCtl) break;
  414.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  415.         display  = (*listData)->mode;
  416.         if (display & clActive) break;
  417.     }
  418.     return(list);
  419. }
  420.  
  421.  
  422.  
  423. /*****************************************************************************/
  424.  
  425.  
  426.  
  427. /* This determines if a List control was clicked on directly.  This does
  428. ** not determine if a related scrollbar was clicked on.  If a List
  429. ** control was clicked on, then true is returned, as well as the List
  430. ** handle and the handle to the view control.
  431. */
  432.  
  433. #pragma segment Controls
  434. Boolean    CLFindCtl(WindowPtr window, EventRecord *event,
  435.                   ListHandle *listHndl, ControlHandle *ctlHit)
  436. {
  437.     WindowPtr        oldPort;
  438.     Point            mouseLoc;
  439.  
  440.     if (window) {
  441.         GetPort(&oldPort);
  442.         SetPort(window);
  443.         mouseLoc = event->where;
  444.         GlobalToLocal(&mouseLoc);
  445.         SetPort(oldPort);
  446.  
  447.         gFoundLHndl = nil;
  448.         FindControl(mouseLoc, window, ctlHit);
  449.         if (*listHndl = gFoundLHndl) return(true);
  450.     }
  451.  
  452.     *ctlHit = nil;
  453.     return(false);
  454. }
  455.  
  456.  
  457.  
  458. /*****************************************************************************/
  459.  
  460.  
  461.  
  462. /* Find the List record that is related to the indicated scrollbar. */
  463.  
  464. #pragma segment Controls
  465. ListHandle    CLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  466. {
  467.     WindowPtr        window;
  468.     ControlHandle    viewCtl;
  469.     ListHandle        list;
  470.  
  471.     window = (*scrollCtl)->contrlOwner;
  472.  
  473.     for (*retCtl = viewCtl = nil;;) {
  474.         viewCtl = CLNext(window, &list, viewCtl);
  475.         if (!viewCtl) return(nil);
  476.         list = (ListHandle)GetCRefCon(viewCtl);
  477.         if (
  478.             ((*list)->vScroll == scrollCtl) || 
  479.             ((*list)->hScroll == scrollCtl)
  480.         ) {
  481.             *retCtl = viewCtl;
  482.             return(list);
  483.         }
  484.     }
  485. }
  486.  
  487.  
  488.  
  489. /*****************************************************************************/
  490.  
  491.  
  492.  
  493. #pragma segment Controls
  494. short    CLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  495. {
  496.     short    loc;
  497.     Point    cell;
  498.  
  499.     if (!listHndl) return(-1);
  500.  
  501.     loc = CLRowOrColSearch(listHndl, data, dataLen, row, col);
  502.  
  503.     if (row == -1) {
  504.         LAddRow(1, cell.v = loc, listHndl);
  505.         cell.h = col;
  506.     }
  507.     else {
  508.         LAddColumn(1, cell.h = loc, listHndl);
  509.         cell.v = row;
  510.     }
  511.  
  512.     LSetCell(data, dataLen, cell, listHndl);
  513.     return(loc);
  514. }
  515.  
  516.  
  517.  
  518. /*****************************************************************************/
  519.  
  520.  
  521.  
  522. /* See if the keypress event applies to the List control, and if it does,
  523. ** handle it and return true.
  524. */
  525.  
  526. #pragma segment Controls
  527. Boolean    CLKey(EventRecord *event)
  528. {
  529.     WindowPtr        window;
  530.     ListHandle        list;
  531.     ControlHandle    listCtl;
  532.     short            key, mode;
  533.     Point            cell, dcell;
  534.     Rect            bnds, visCells;
  535.     CLDataHndl        listData;
  536.  
  537.     if (list = CLFindActive(window = FrontWindow())) {
  538.  
  539.         listCtl  = CLViewFromList(list);
  540.         listData = (CLDataHndl)(*listCtl)->contrlData;
  541.         mode     = (*listData)->mode;
  542.         if (!(mode & clKeyPos)) return(false);
  543.  
  544.         bnds = (*list)->dataBounds;
  545.         if (bnds.top == bnds.bottom) return(true);
  546.         if (bnds.left == bnds.right) return(true);
  547.             /* The list is empty, so whatever was typed has been "handled". */
  548.  
  549.         cell.h = bnds.left;
  550.         cell.v = bnds.top;
  551.         key = event->message & charCodeMask;
  552.  
  553.         if ((key >= kLeftArrow) && (key <= kDownArrow)) {
  554.             if (LGetSelect(true, &cell, list)) key -= kPrevSel;
  555.             switch (key) {
  556.                 case kLeftArrow - kPrevSel:
  557.                     if (cell.h > bnds.left) --cell.h;
  558.                     break;
  559.                 case kRightArrow - kPrevSel:
  560.                     if (cell.h < bnds.left - 1) ++cell.h;
  561.                     break;
  562.                 case kUpArrow - kPrevSel:
  563.                     if (cell.v > bnds.top) --cell.v;
  564.                     break;
  565.                 case kDownArrow - kPrevSel:
  566.                     if (cell.v < bnds.bottom - 1) ++cell.v;
  567.                     break;
  568.                 case kLeftArrow:
  569.                     if (bnds.top < bnds.bottom - 1) return(true);
  570.                         /* With no previous selection, if the list has
  571.                         ** more than one row, we don't know which to use.
  572.                         ** Selection is indeterminate, so do nothing. */
  573.                     cell.v = bnds.top;
  574.                     cell.h = bnds.right - 1;
  575.                     break;
  576.                 case kRightArrow:
  577.                     if (bnds.top < bnds.bottom - 1) return(true);
  578.                         /* With no previous selection, if the list has
  579.                         ** more than one row, we don't know which to use.
  580.                         ** Selection is indeterminate, so do nothing. */
  581.                     cell.v = bnds.top;
  582.                     cell.h = bnds.left;
  583.                     break;
  584.                 case kUpArrow:
  585.                     if (bnds.left < bnds.right - 1) return(true);
  586.                         /* With no previous selection, if the list has
  587.                         ** more than one row, we don't know which to use.
  588.                         ** Selection is indeterminate, so do nothing. */
  589.                     cell.h = bnds.left;
  590.                     cell.v = bnds.bottom - 1;
  591.                     break;
  592.                 case kDownArrow:
  593.                     if (bnds.left < bnds.right - 1) return(true);
  594.                         /* With no previous selection, if the list has
  595.                         ** more than one row, we don't know which to use.
  596.                         ** Selection is indeterminate, so do nothing. */
  597.                     cell.h = bnds.left;
  598.                     cell.v = bnds.top;
  599.                     break;
  600.             }
  601.         }
  602.         else {
  603.             if ((bnds.right - bnds.left > 1) && (bnds.bottom - bnds.top > 1)) return(true);
  604.             if (lastKeyTime + 45 < TickCount()) {        /* Reset character collection. */
  605.                 listPosTextLen = 0;
  606.                 lastKeyTime = TickCount();
  607.             }
  608.             if (listPosTextLen < kListPosTextLen) listPosText[listPosTextLen++] = key;
  609.             LSearch(listPosText, listPosTextLen, MyIntlCompare, &cell, list);
  610.                 /* Find the cell closest to the query test. */
  611.             if (cell.h >= bnds.right)  cell.h = bnds.right - 1;
  612.             if (cell.v >= bnds.bottom) cell.v = bnds.bottom - 1;
  613.         }
  614.  
  615.         LSetSelect(true, cell, list);        /* Select cell that is closest. */
  616.         dcell.h = bnds.left;
  617.         dcell.v = bnds.top;
  618.         for (;;) {                /* Deselect old cells. */
  619.             if (!LGetSelect(true, &dcell, list)) break;
  620.             if ((dcell.h == cell.h) && (dcell.v == cell.v)) {
  621.                 if (++dcell.h >= bnds.right) {
  622.                     dcell.h = bnds.left;
  623.                     ++dcell.v;
  624.                 }
  625.             }
  626.             else LSetSelect(false, dcell, list);
  627.         }
  628.  
  629.         visCells = (*list)->visible;
  630.         if (PtInRect(cell, &visCells)) return(true);        /* Already in view. */
  631.  
  632.         if (
  633.             (key != (kDownArrow - kPrevSel)) || 
  634.             (cell.v < visCells.top) || 
  635.             (cell.h < visCells.left)
  636.         ) {
  637.             LAutoScroll(list);        /* Let List manager scroll into view. */
  638.             return(true);            /* It handles these cases correctly.  */
  639.         }
  640.  
  641.         LScroll(cell.h - --visCells.right, cell.v - --visCells.bottom, list);
  642.             /* Scroll into view the way we want it done. */
  643.  
  644.         return(true);
  645.     }
  646. }
  647.  
  648.  
  649.  
  650. /*****************************************************************************/
  651.  
  652.  
  653.  
  654. /* Create a new List control.  See the comments at the beginning of this
  655. ** file for more information.
  656. */
  657.  
  658. #pragma segment Controls
  659. ListHandle    CLNew(short viewID, Rect *vRect, short numRows, short numCols, short cellHeight,
  660.                   short cellWidth, short theLProc, WindowPtr window, short mode)
  661. {
  662.     WindowPtr        oldPort;
  663.     Rect            viewRect, dataBnds;
  664.     Point            cellSize;
  665.     ListHandle        list;
  666.     Boolean            err;
  667.     ControlHandle    viewCtl;
  668.     cdefRsrcJMPHndl    cdefRsrc;
  669.     Boolean            drawIt, hScroll, vScroll;
  670.     CLDataHndl        listData;
  671.  
  672.     theViewID = viewID;        /* Keep viewID that was passed in. */
  673.  
  674.     GetPort(&oldPort);
  675.     SetPort(window);
  676.  
  677.     viewRect = *vRect;
  678.     dataBnds.top = dataBnds.left = 0;
  679.     dataBnds.right  = numCols;
  680.     dataBnds.bottom = numRows;
  681.  
  682.     cellSize.h = cellWidth;
  683.     cellSize.v = cellHeight;
  684.  
  685.     drawIt  = (mode & clDrawIt);
  686.     hScroll = (mode & clHScroll);
  687.     vScroll = (mode & clVScroll);
  688.     list = LNew(&viewRect, &dataBnds, cellSize, theLProc, window, drawIt, false, hScroll, vScroll);
  689.  
  690.     err = false;
  691.     if (list) {        /* If we were able to create the List record... */
  692.  
  693.         cdefRsrc = (cdefRsrcJMPHndl)GetResource('CDEF', viewID);
  694.         (*cdefRsrc)->jmpAddress = (long)CLCtl;
  695.         FlushInstructionCache();
  696.             /* Make sure that instruction caches don't kill us. */
  697.  
  698.         viewCtl = NewControl(window, &viewRect, nil, false, 0, 0, 0,
  699.                              viewID * 16, (long)list);
  700.             /* Use our custom view cdef.  It's wierd, but it's small. */
  701.             /* We have to create the control initially invisible because we haven't */
  702.             /* initialized all of the data needed by the update procedure.  If it   */
  703.             /* is created visible, the update procedure will be immediately called, */
  704.             /* and this would be very bad to do until all of the data is there.     */
  705.  
  706.         mode &= (0xFFFF - clDrawIt);
  707.  
  708.         if (!viewCtl) err = true;
  709.         else {
  710.             (*viewCtl)->contrlData = nil;
  711.             if (listData = (CLDataHndl)NewHandle(sizeof(CLDataRec))) {
  712.                 (*listData)->mode   = mode;
  713.                 (*listData)->txFont = window->txFont;
  714.                 (*listData)->txFace = window->txFace;
  715.                 (*listData)->txMode = window->txMode;
  716.                 (*listData)->txSize = window->txSize;
  717.                 (*viewCtl)->contrlData = (Handle)listData;
  718.                 ShowControl(viewCtl);
  719.                     /* Now that the data is initialized, we can show the control. */
  720.             }
  721.             else err = true;
  722.  
  723.             if (mode & clActive) CLActivate(true, list);
  724.         }
  725.     }
  726.     else err = true;
  727.  
  728.     SetPort(oldPort);
  729.  
  730.     if (err) {        /* Oops.  Somebody wasn't happy. */
  731.         if (viewCtl) DisposeControl(viewCtl);
  732.                 /* This also disposes of the List handle! */
  733.         else
  734.             if (list) LDispose(list);
  735.                 /* We have to dispose of the List handle ourselves if
  736.                 ** creating the view control failed. */
  737.  
  738.         list = nil;        /* Return that there is no List control. */
  739.     }
  740.  
  741.     return(list);
  742. }
  743.  
  744.  
  745.  
  746. /*****************************************************************************/
  747.  
  748.  
  749.  
  750. /* Get the next List control in the window.  You pass it a control handle
  751. ** for the view control, or nil to start at the beginning of the window.
  752. ** It returns both a List handle and the view control handle for that
  753. ** List record.  If none is found, nil is returned.  This allows you to
  754. ** repeatedly call this function and walk through all the List controls
  755. ** in a window.
  756. */
  757.  
  758. #pragma segment Controls
  759. ControlHandle    CLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl)
  760. {
  761.     short    defProcID;
  762.     ResType    defProcType;
  763.     Str255    defProcName;
  764.  
  765.     *listHndl = nil;
  766.     if (!window) return(nil);
  767.  
  768.     if (!ctl)
  769.         ctl = ((WindowPeek)window)->controlList;
  770.     else
  771.         ctl = (*ctl)->nextControl;
  772.  
  773.     while (ctl) {
  774.         defProcID = !theViewID;
  775.         GetResInfo((*ctl)->contrlDefProc, &defProcID, &defProcType, defProcName);
  776.         if (defProcID == theViewID) {
  777.             *listHndl = (ListHandle)GetCRefCon(ctl);
  778.             break;
  779.         }
  780.         ctl = (*ctl)->nextControl;
  781.     }
  782.  
  783.     return(ctl);
  784. }
  785.  
  786.  
  787.  
  788. /*****************************************************************************/
  789.  
  790.  
  791.  
  792. #pragma segment Controls
  793. void    CLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  794.                 short leftEdge, Rect *drawRct)
  795. {
  796.     Rect        dataBnds, keepView, keepVis;
  797.     Point        csize;
  798.     short        h, v;
  799.     RgnHandle    rgn;
  800.  
  801.     if (!listHndl) return;
  802.  
  803.     dataBnds = (*listHndl)->dataBounds;
  804.     if ((*col < dataBnds.left) || (*col >= dataBnds.right)) *col = leftEdge;
  805.     if (*row < dataBnds.top) *row = dataBnds.top;
  806.     if (*row >= dataBnds.bottom) {
  807.         *row = -1;
  808.         return;
  809.     }
  810.  
  811.     keepView = (*listHndl)->rView;
  812.     csize    = (*listHndl)->cellSize;
  813.     keepVis  = (*listHndl)->visible;
  814.  
  815.     h = (drawRct->right - drawRct->left) / csize.h;
  816.     if (!h) ++h;
  817.     v = (drawRct->bottom - drawRct->top) / csize.v;
  818.     if (!v) ++v;
  819.  
  820.     if (*col + h > dataBnds.right)  h = dataBnds.right  - *col;
  821.     if (*row + v > dataBnds.bottom) v = dataBnds.bottom - *row;
  822.  
  823.     drawRct->bottom = drawRct->top + v * csize.v;
  824.  
  825.     (*listHndl)->rView = *drawRct;
  826.     (*listHndl)->visible.right  = ((*listHndl)->visible.left = *col) + h;
  827.     (*listHndl)->visible.bottom = ((*listHndl)->visible.top  = *row) + v;
  828.  
  829.     if (!(rgn = clipRgn)) {
  830.         rgn = NewRgn();
  831.         RectRgn(rgn, drawRct);
  832.     }
  833.     CLUpdate(rgn, listHndl);
  834.     if (!clipRgn) DisposeRgn(rgn);
  835.  
  836.     (*listHndl)->rView   = keepView;
  837.     (*listHndl)->visible = keepVis;
  838.  
  839.     *col += h;
  840.     if (*col >= dataBnds.right) {
  841.         *col = leftEdge;
  842.         *row += v;
  843.         if (*row >= dataBnds.bottom) *row = -1;
  844.     }
  845. }
  846.  
  847.  
  848.  
  849. /*****************************************************************************/
  850.  
  851.  
  852.  
  853. #pragma segment Controls
  854. short    CLRowOrColSearch(ListHandle listHndl, char *data, short dataLen, short row, short col)
  855. {
  856.     Rect    dataBnds;
  857.     short    numCells, baseCell, iter, pow, loc, cdataLen;
  858.     Point    cell;
  859.     Str255    cdata;
  860.  
  861.     if (!listHndl) return(-1);
  862.  
  863.     dataBnds = (*listHndl)->dataBounds;
  864.     if (row == -1)
  865.         numCells = dataBnds.bottom - (baseCell = dataBnds.top);
  866.     else
  867.         numCells = dataBnds.right  - (baseCell = dataBnds.left);
  868.             /* Get some reference info on the size/start of the list. */
  869.  
  870.     cell.v = cell.h = 0;
  871.     if (numCells) {
  872.         if (row != -1) cell.v = row;
  873.         if (col != -1) cell.h = col;
  874.         for (pow = 1; pow < numCells; ++iter, pow <<= 1);
  875.         pow >>= 1;        /* pow = 2^n such that pow < numCells. */
  876.  
  877.         for (loc = pow; pow;) {        /* Do binary search for where to insert. */
  878.             if (loc >= numCells) loc = numCells - 1;    /* Off the end is bad. */
  879.             if (row == -1)
  880.                 cell.v = baseCell + loc;
  881.             else
  882.                 cell.h = baseCell + loc;
  883.             pow >>= 1;
  884.  
  885.             cdataLen = 255;
  886.             LGetCell(cdata, &cdataLen, cell, listHndl);
  887.                 /* Get cell data to compare against. */
  888.  
  889.             loc += (pow * IUMagString(data, cdata, dataLen, cdataLen));
  890.                 /* Adjust location based on compare result. */
  891.         }
  892.  
  893.         /* The binary search got us close, but not exact.  We may be off by one
  894.         ** in either direction.  (The binary search can't position in front of
  895.         ** the first cell in the list, for example.)  Do a linear compare from
  896.         ** this point to find the correct cell we should insert in front of. */
  897.  
  898.         if (loc) --loc;        /* Start linear search one back. */
  899.         for (;; ++loc) {
  900.             if (row == -1) cell.v = baseCell + loc;
  901.             else           cell.h = baseCell + loc;
  902.             if (loc >= numCells) break;
  903.             cdataLen = 255;
  904.             LGetCell(cdata, &cdataLen, cell, listHndl);
  905.             if (IUMagString(data, cdata, dataLen, cdataLen) < 1) break;
  906.                 /* If we are less than or equal to this cell, we have
  907.                 ** found our insertion point, so break. */
  908.         }
  909.     }
  910.  
  911.     if (row == -1) return(cell.v);
  912.     else           return(cell.h);
  913. }
  914.  
  915.  
  916.  
  917. /*****************************************************************************/
  918.  
  919.  
  920.  
  921. #pragma segment Controls
  922. void    CLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  923. {
  924.     WindowPtr        thePort, listPort;
  925.     short            txFont, txMode, txSize;
  926.     Style            txFace;
  927.     CLDataHndl        listData;
  928.     ControlHandle    ctl;
  929.  
  930.     if (listHndl) {
  931.  
  932.         if (ctl = CLViewFromList(listHndl)) {
  933.     
  934.             GetPort(&thePort);
  935.  
  936.             txFont = thePort->txFont;
  937.             txFace = thePort->txFace;
  938.             txMode = thePort->txMode;
  939.             txSize = thePort->txSize;
  940.  
  941.             listData = (CLDataHndl)(*ctl)->contrlData;
  942.             TextFont((*listData)->txFont);
  943.             TextFace((*listData)->txFace);
  944.             TextMode((*listData)->txMode);
  945.             TextSize((*listData)->txSize);
  946.  
  947.             listPort = (*listHndl)->port;
  948.             (*listHndl)->port = thePort;
  949.             LUpdate(clipRgn, listHndl);
  950.             (*listHndl)->port = listPort;
  951.  
  952.             TextFont(txFont);
  953.             TextFace(txFace);
  954.             TextMode(txMode);
  955.             TextSize(txSize);
  956.         }
  957.     }
  958. }
  959.  
  960.  
  961.  
  962. /*****************************************************************************/
  963.  
  964.  
  965.  
  966. /* Return the control handle for the view control that owns the List
  967. ** record.  Use this to find the view to do customizations such as changing
  968. ** the update procedure for this List control.
  969. */
  970.  
  971. #pragma segment Controls
  972. ControlHandle    CLViewFromList(ListHandle listHndl)
  973. {
  974.     WindowPtr        window;
  975.     ControlHandle    viewCtl;
  976.     ListHandle        list;
  977.  
  978.     if (!listHndl) return(nil);
  979.  
  980.     window = (WindowPtr)(*listHndl)->port;
  981.  
  982.     for (viewCtl = nil;;) {
  983.         viewCtl = CLNext(window, &list, viewCtl);
  984.         if ((!viewCtl) || (list == listHndl)) return(viewCtl);
  985.     }
  986. }
  987.  
  988.  
  989.  
  990. /*****************************************************************************/
  991.  
  992.  
  993.  
  994. /* This window is becoming active or inactive.  The borders of the List
  995. ** controls need to be redrawn due to this.  For each List control in the
  996. ** window, redraw the active border.
  997. */
  998.  
  999. #pragma segment Controls
  1000. void    CLWindActivate(WindowPtr window)
  1001. {
  1002.     ControlHandle    viewCtl;
  1003.     ListHandle        list;
  1004.  
  1005.     if (window) {
  1006.  
  1007.         for (viewCtl = nil;;) {
  1008.             viewCtl = CLNext(window, &list, viewCtl);
  1009.             if (!viewCtl) break;
  1010.             CLBorderDraw(list);
  1011.         }
  1012.  
  1013.         lastKeyTime = 0;    /* Restart the entry collection for Lists. */
  1014.     }
  1015. }
  1016.  
  1017.  
  1018.  
  1019. /*****************************************************************************/
  1020.  
  1021.  
  1022.  
  1023. #pragma segment Controls
  1024. pascal short MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  1025. {
  1026.     short    cmp;
  1027.  
  1028.     cmp = IUMagString(aPtr, bPtr, aLen, bLen);
  1029.     if (cmp == -1) return(1);
  1030.     else           return(0);
  1031. }
  1032.  
  1033.  
  1034.  
  1035.